unit ec_base;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, fpjson, database,
  LCLTranslator, ExtCtrls, Buttons;

type

  { TECBaseFrame }

  TECBaseFrame = class(TForm)
    EcCancelBitBtn: TBitBtn;
    EcOkBitBtn: TBitBtn;
    EcResultLineBevel: TBevel;
    EcResultPanel: TPanel;
    procedure FormCreate(Sender: TObject);
  private
  public
    Db: TDatabase;
    EcList: TJSONArray;
    ListIndex: Integer;
    Event, Troop: TJSONObject;
    Indent: Integer;

    ResultingCommand: TJSONArray;


    procedure SetUp(ADb: TDatabase; AEvent: TJSONObject = nil;
      ATroop: TJSONObject = nil);

    function GetFirstCommand: TJSONObject; inline;
    function GetFirstParams: TJSONArray; inline;
    function GetFirstIndent: Integer; inline;

    procedure InitNew; virtual;
    procedure InitExisting; virtual;

    procedure MakeResultingCommand; virtual;
    function SingleLineResultingCommand(Code: Integer; Parameters: TJSONArray): TJSONArray;

    function ShowWithNew(AIndent: Integer): Boolean; virtual;
    function ShowWithExisting(AEcList: TJSONArray;
      AIndex: Integer): Boolean; virtual;

    procedure ReplaceEditedCommand(OldStart: Integer; OldLength: Integer); virtual;
    procedure AddNewCommand(AEcList: TJSONArray; StartFrom: Integer); virtual;
    function GetLength: Integer; virtual;

    procedure UpdateLanguage(Code: String); virtual;

    class procedure AddCommandToList(Command, AEcList: TJSONArray; StartFrom: Integer); static;

    function FindStartOfSubcommands(Index: Integer): Integer; virtual;
    procedure AddExitingSubCommands(NewCmd: TJSONArray; Index: Integer);

    destructor Destroy; override;
  end;

var
  ECBaseFrame: TECBaseFrame;

implementation

{$R *.lfm}

uses
  StdCtrls, languagehelper;

{ TECBaseFrame }

procedure TECBaseFrame.FormCreate(Sender: TObject);
  procedure SwapButtons(A, B: TCustomButton);
  var
    Tmp: Integer;
  begin
    Tmp := A.Left;
    A.Left := B.Left;
    B.Left := Tmp;

    Tmp := A.TabOrder;
    A.TabOrder := B.TabOrder;
    B.TabOrder := Tmp;
  end;
begin

  { On Gnome, the button order is: Cancel OK
    On Mac, the button order is: Cancel OK
    On Windows, the button oder is: OK Cancel
    On KDE, the button order is: OK Cancel }
  {$IF Defined(LCLGtk) or Defined(LCLGtk2) or Defined(LCLGtk3) or Defined(LCLCarbon) or Defined(LCLCocoa)}
    SwapButtons(EcOkBitBtn, EcCancelBitBtn);
  {$ENDIF}
end;

procedure TECBaseFrame.SetUp(ADb: TDatabase; AEvent: TJSONObject;
  ATroop: TJSONObject);
begin
  Db := ADb;
  Event := AEvent;
  Troop := ATroop;
end;

function TECBaseFrame.GetFirstCommand: TJSONObject;
begin
  GetFirstCommand := nil;
  if (EcList <> nil) and (ListIndex < EcList.Count) then
     GetFirstCommand := EcList.Objects[ListIndex];
end;

function TECBaseFrame.GetFirstParams: TJSONArray;
var
  FirstEc: TJSONObject;
  Params: TJSONArray;
begin
  GetFirstParams := nil;

  FirstEc := GetFirstCommand;
  if (FirstEc <> nil) and FirstEc.Find('parameters', Params) then
     GetFirstParams := Params;

end;

function TECBaseFrame.GetFirstIndent: Integer;
var
  FirstEc: TJSONObject;
  FirstIndent: TJSONNumber;
begin
  GetFirstIndent := 0;

  FirstEc := GetFirstCommand;
  if (FirstEc <> nil) and FirstEc.Find('indent', FirstIndent) then
     GetFirstIndent := FirstIndent.AsInteger;
end;

procedure TECBaseFrame.InitNew;
begin
  //TODO
end;

procedure TECBaseFrame.InitExisting;
begin
  //TODO
end;

procedure TECBaseFrame.MakeResultingCommand;
begin
  { Override me }
  ResultingCommand := nil;
end;

function TECBaseFrame.SingleLineResultingCommand(Code: Integer;
  Parameters: TJSONArray): TJSONArray;
begin
  SingleLineResultingCommand := TJSONArray.Create([
    TJSONObject.Create([
      'code', Code,
      'indent', Indent,
      'parameters', Parameters
    ])
  ]);
end;

function TECBaseFrame.ShowWithNew(AIndent: Integer): Boolean;
begin
  Indent := AIndent;
  InitNew;
  ShowModal;
  ShowWithNew := ModalResult = mrOK;
end;

function TECBaseFrame.ShowWithExisting(AEcList: TJSONArray; AIndex: Integer
  ): Boolean;
begin
  EcList := AEcList;
  ListIndex := AIndex;
  Indent := GetFirstIndent;

  InitExisting;
  ShowModal;
  ShowWithExisting := ModalResult = mrOK;
end;

procedure TECBaseFrame.ReplaceEditedCommand(OldStart: Integer; OldLength: Integer);
var
  NewLength, I: Integer;
begin
  if ResultingCommand = nil then
     MakeResultingCommand;

  NewLength := GetLength;

  if OldLength > NewLength then
   for I := 1 to OldLength - NewLength do
      EcList.Delete(OldStart + 1)
  else if OldLength < NewLength then
   for I := 1 to NewLength - OldLength do
      EcList.Insert(OldStart + 1);

  for I := ResultingCommand.Count -1 downto 0 do
    EcList[OldStart + I] := ResultingCommand.Extract(I);
end;

procedure TECBaseFrame.AddNewCommand(AEcList: TJSONArray; StartFrom: Integer);
begin
  EcList := AEcList;
  if ResultingCommand = nil then
     MakeResultingCommand;

  AddCommandToList(ResultingCommand, AEcList, StartFrom);
end;

class procedure TECBaseFrame.AddCommandToList(Command, AEcList: TJSONArray; StartFrom: Integer);
var
  EcLength, I: Integer;
begin
  if Command <> nil then
    EcLength := Command.Count;
    for I := 1 to EcLength do
      AEcList.Insert(StartFrom);

    for I := EcLength - 1 downto 0 do
      AEcList[StartFrom + I] := Command.Extract(I)
end;

function TECBaseFrame.FindStartOfSubcommands(Index: Integer): Integer;
begin
  (* Override to find a command-specific inner list.
  Return -1 if it wasn't found. *)
  Result := ListIndex;
end;

procedure TECBaseFrame.AddExitingSubCommands(NewCmd: TJSONArray; Index: Integer
  );
var
  I: Integer;
  Cmd: TJSONObject;
begin
  I := FindStartOfSubcommands(Index) + 1;

  if (I <= 0) or (EcList = nil) or (EcList.JSONType <> jtArray) then begin
    NewCmd.Add(TJSONObject.Create([
      'code', 0,
      'indent', Indent + 1,
      'parameters', TJSONArray.Create()
    ]));
    Exit();
  end else while I < EcList.Count do begin
    Cmd := EcList.Objects[I];
    if (Cmd.Integers['indent'] <= Indent) then
     Break;
    NewCmd.Add(Cmd.Clone);

    Inc(I);
  end;
end;

function TECBaseFrame.GetLength: Integer;
begin
  if ResultingCommand = nil then
     MakeResultingCommand;

  if ResultingCommand <> nil then
    GetLength := ResultingCommand.Count
  else
    GetLength := 0
end;

procedure TECBaseFrame.UpdateLanguage(Code: String);
begin
  if IsLanguageRtl(Code) then begin
    FlipChildren(True);
    BiDiMode := bdRightToLeft;
  end else
    BiDiMode := bdLeftToRight;
end;

destructor TECBaseFrame.Destroy;
begin
  if ResultingCommand <> nil then
    FreeAndNil(ResultingCommand);

  inherited Destroy;
end;

end.

